package redis

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/worklz/yunj-blog-go-micro-common/util"
	"strings"
	"time"

	"github.com/redis/go-redis/v9"
)

// FullKey 获取redis key完整名称
func FullKey(key string) string {
	prefix := Config.Prefix
	if strings.HasPrefix(key, prefix) {
		return key
	} else {
		return fmt.Sprintf("%s%s", prefix, key)
	}
}

// 是否存在
func Exists(ctx context.Context, key string) (res bool, err error) {
	key = FullKey(key)
	exists, err := Client.Exists(ctx, key).Result()
	if err != nil {
		return
	}
	res = exists > 0
	return
}

// 获取字符串
// 若返回结果[]byte长度为0，也可认为无缓存值
func Get(ctx context.Context, key string) (value string, err error) {
	key = FullKey(key)
	value, redisErr := Client.Get(ctx, key).Result()
	if redisErr != nil && !errors.Is(redisErr, redis.Nil) {
		err = redisErr
	}
	return
}

// 设置缓存
func Set(ctx context.Context, key string, val interface{}, ttl time.Duration) (err error) {
	var value string
	if valStr, ok := val.(string); ok {
		value = valStr
	} else {
		valStr, toStrErr := util.ToString(val)
		if toStrErr == nil {
			value = valStr
		} else {
			valStr, err = util.ToJson(val)
			if err != nil {
				err = fmt.Errorf("缓存值不能转换为字符串！%v", err)
				return
			}
			value = valStr
		}
	}
	key = FullKey(key)
	err = Client.Set(ctx, key, value, ttl).Err()
	if err != nil {
		err = fmt.Errorf("设缓存字符串值失败: %v", err)
		return
	}
	return
}

// 递增
func Incr(ctx context.Context, key string) (int64, error) {
	key = FullKey(key)
	result, err := Client.Incr(ctx, key).Result()
	if err != nil {
		return 0, err
	}
	return result, nil
}

// 删除
// 可以传入string、[]string
func Del(ctx context.Context, key ...interface{}) (err error) {
	keys := []string{}
	if len(key) <= 0 {
		return
	} else if len(key) == 1 {
		if keyStr, ok := key[0].(string); ok {
			keys = append(keys, keyStr)
		} else if keyArr, ok := key[0].([]string); ok {
			keys = keyArr
		} else {
			err = errors.New("redis.Del()方法参数错误")
			return
		}
	} else {
		for _, v := range key {
			if !util.IsScalar(v) {
				err = errors.New("redis.Del()方法参数类型错误")
				return
			}
			if keyStr, ok := v.(string); ok {
				keys = append(keys, keyStr)
			}
		}
	}

	delKeys := make([]string, len(keys))
	for k, v := range keys {
		delKeys[k] = FullKey(v)
	}
	_, err = Client.Del(ctx, delKeys...).Result()
	if err != nil {
		err = fmt.Errorf("缓存删除失败！%v", err)
		return
	}
	return
}

// 获取匹配的所有key
// 调用示例：Keys("article:*")、Keys("article:*:*")、Keys("*article:*")
func Keys(ctx context.Context, pattern string) (res []string, err error) {
	pattern = FullKey(pattern)
	cursor := uint64(0)
	for {
		// 调用 Scan 方法，指定每次扫描的条数
		keys, cursor := Client.Scan(ctx, cursor, pattern, 1000).Val() // 每次扫描1000条
		res = append(res, keys...)
		if cursor == 0 {
			break
		}
	}
	return
}

// 入队
func LPush(ctx context.Context, key string, values ...any) (err error) {
	key = FullKey(key)
	err = Client.LPush(ctx, key, values...).Err()
	return
}

// 出队
func RPop(ctx context.Context, key string) (value string, err error) {
	key = FullKey(key)
	value, err = Client.RPop(ctx, key).Result()
	return
}

// 出队
// 实现阻塞式消费（队列空时等待），适用于单消费者或多消费者竞争模式。
func BRPop(ctx context.Context, timeout time.Duration, key string) (value string, err error) {
	key = FullKey(key)
	res, err := Client.BRPop(ctx, timeout, key).Result()
	if len(res) > 1 {
		// res[0] 是队列键名，res[1] 是消息内容
		value = res[1]
	}
	return
}

// 获取队列长度
func LLen(ctx context.Context, key string) (length int64, err error) {
	key = FullKey(key)
	length, err = Client.LLen(ctx, key).Result()
	return
}
